using AnimCollections;
using System;
using Unity.Collections;

namespace DA.AnimGraph {
    public struct StateMachineTransition {
        public int targetState;
        public float duration;

        public StateMachineTransition(in TransitionDesc desc) {
            targetState = desc.targetState;
            duration = desc.duration;
        }
    }
    
    // 添加默认过渡（大概只需要定义时间）
    // anyState 要不要不支持 exitTime 方式（目前没限制）
    internal struct StateMachineTransitionInfo : IDisposable {
        public NativeSimpleArray<StateMachineTransition> transitions;
        public NativeSimpleArray<TransitionConditionLink> conditions;
        public NativeSimpleArray<int> stateToFirstTransitionsIndex;
        public NativeSimpleArray<int> transitionsToFirstConditionIndex;
        private int anyStateToFirstTransitionsIndex;
        private int anyStateToEndTransitionsIndex;

        public unsafe StateMachineTransitionInfo(in TransitionDesc[] transitionDescs, int stateCount, ref GraphBuffer buffer) {
            // 需要按sourceStateId从小到大排序好，有编辑器则在保存时进行,any 的排在最前面
            Array.Sort(transitionDescs, (a, b) => {
                if (a.sourceState == b.sourceState) return 0;
                return a.sourceState < b.sourceState ? -1 : 1;
            });

            int transitionCount = transitionDescs.Length;
            int conditionsCount = TransitionUtility.SumConditions(transitionDescs);

            buffer.MallocNativeSimpleArray(out transitions, transitionCount);
            buffer.MallocNativeSimpleArray(out conditions, conditionsCount);

            buffer.MallocNativeSimpleArray(out stateToFirstTransitionsIndex, stateCount + 1);
            buffer.MallocNativeSimpleArray(out transitionsToFirstConditionIndex, transitionCount + 1);

            int transIndex = 0;
            anyStateToFirstTransitionsIndex = -1;
            anyStateToEndTransitionsIndex = -1;
            int stateIter = -1;
            for (; stateIter < stateCount; stateIter++) {
                if (transIndex == transitionCount) break;

                ref TransitionDesc transDesc = ref transitionDescs[transIndex];
                if (transDesc.sourceState > stateIter) {
                    if (stateIter != -1) {
                        stateToFirstTransitionsIndex[stateIter] = -1;
                    }
                    continue;
                } else if (transDesc.sourceState == stateIter) {
                    if (stateIter != -1) {
                        stateToFirstTransitionsIndex[stateIter] = transIndex;
                        if (anyStateToEndTransitionsIndex < 0) {
                            anyStateToEndTransitionsIndex = transIndex;
                        }
                    } else {
                        anyStateToFirstTransitionsIndex = transIndex;
                    }
                }

                for (; transIndex < transitionCount; transIndex++) {
                    if (transitionDescs[transIndex].sourceState != transDesc.sourceState) {
                        break;
                    }
                }
            }
            if (anyStateToEndTransitionsIndex < 0) {
                anyStateToEndTransitionsIndex = transitionCount;
            }
            for (; stateIter < stateToFirstTransitionsIndex.Length; stateIter++) {
                if (stateIter < 0) continue;
                stateToFirstTransitionsIndex[stateIter] = transitionCount;
            }

            int condIndex = 0;
            for (int i = 0; i < transitionCount; i++) {
                ref TransitionDesc transDesc = ref transitionDescs[i];

                transitions[i] = new StateMachineTransition(transDesc);
                if (transDesc.conditions.Length == 0) {
                    transitionsToFirstConditionIndex[i] = -1;
                } else {
                    transitionsToFirstConditionIndex[i] = condIndex;
                }

                for (int j = 0; j < transDesc.conditions.Length; j++) {
                    ref var condDesc = ref transDesc.conditions[j];

                    if (TransitionUtility.CreateCondition(out TransitionConditionLink link, condDesc, ref buffer)) {
                        conditions[condIndex] = link;
                        ++condIndex;
                    }

                }
            }
            transitionsToFirstConditionIndex[^1] = conditionsCount;
        }

        public void Dispose() {
            for (int i = 0; i < conditions.Length; i++) {
                conditions[i].Dispose();
            }

            transitions.Dispose();
            conditions.Dispose();
            stateToFirstTransitionsIndex.Dispose();
            transitionsToFirstConditionIndex.Dispose();
        }

        internal bool CheckState(int state, in NodeLink node, in GraphContext graphContext, out StateMachineTransition transition) {
            int startTransIndex = stateToFirstTransitionsIndex[state];
            if (startTransIndex >= 0) {
                int endTransIndex = stateToFirstTransitionsIndex[state + 1];

                for (int i = startTransIndex; i < endTransIndex; i++) {
                    if (CheckTransition(i, node, graphContext)) {
                        transition = transitions[i];
                        return true;
                    }
                }
            }
            return CheckAnyState(node, graphContext, out transition);
        }

        bool CheckAnyState(in NodeLink node, in GraphContext graphContext, out StateMachineTransition transition) {
            if (anyStateToFirstTransitionsIndex != -1) {
                for (int i = anyStateToFirstTransitionsIndex; i < anyStateToEndTransitionsIndex; i++) {
                    if (CheckTransition(i, node, graphContext)) {
                        transition = transitions[i];
                        return true;
                    }
                }
            }

            transition = default;
            return false;
        }

        bool CheckTransition(int index, in NodeLink node, in GraphContext graphContext) {
            int startCondIndex = transitionsToFirstConditionIndex[index];
            if (startCondIndex < 0) {
                // 存在过渡但是不存在条件，则以通过处理
                return true;
            }
            int endCondIndex = transitionsToFirstConditionIndex[index + 1];

            bool result = true;
            for (int i = startCondIndex; i < endCondIndex; i++) {
                result = result && CheckCondition(i, node, graphContext);
                if (!result) {
                    return false;
                }
            }

            return true;
        }

        bool CheckCondition(int index, in NodeLink node, in GraphContext graphContext) {
            return conditions[index].Check(node, graphContext);
        }

    }
}
